home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Speccy ClassiX 1998
/
Speccy ClassiX 98.iso
/
amiga_system
/
the_aminet
/
dev
/
gcc
/
ixemulsrc.lha
/
ixemul-41.4
/
library
/
pipe.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-05-28
|
13KB
|
517 lines
/*
* This file is part of ixemul.library for the Amiga.
* Copyright (C) 1991, 1992 Markus M. Wild
* Portions Copyright (C) 1994 Rafael W. Luebbert
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: pipe.c,v 1.4 1994/06/19 15:14:19 rluebbert Exp $
*
* $Log: pipe.c,v $
* Revision 1.4 1994/06/19 15:14:19 rluebbert
* *** empty log message ***
*
* Revision 1.2 1992/07/04 19:21:08 mwild
* (finally..) fix the bug which could cause pipe readers/writers to deadlock
*
* Revision 1.1 1992/05/14 19:55:40 mwild
* Initial revision
*
*/
#define KERNEL
#include "ixemul.h"
#include "kprintf.h"
#include <sys/ioctl.h>
#include "select.h"
/* information for the temporary implementation of pipes.
PIPE: has the big disadvantage that it blocks in the most unpleasent
situations, and doesn't send SIGPIPE to processes that write on
readerless pipes. Unacceptable for this library ;-)) */
#define PIPE_SIZE 5120
struct tmp_pipe {
u_short tp_flags; /* see below */
u_char tp_buffer[PIPE_SIZE];
u_char *tp_reader, *tp_writer; /* buffer pointers.
when tp_reader==tp_writer, no data
is available */
};
#define TPF_NO_READER (1<<0)
#define TPF_NO_WRITER (1<<1)
#define TPF_LOCKED (1<<2)
#define TPF_WANT_LOCK (1<<3)
static int __pread(), __pwrite(), __pselect(), __pioctl(), __pclose();
static struct tmp_pipe *__pinit();
static inline void
__get_pipe (struct file *f)
{
struct tmp_pipe *tp = f->f_tp;
retry:
Forbid ();
for (;;)
{
if (!(tp->tp_flags & TPF_LOCKED))
{
tp->tp_flags &= ~TPF_WANT_LOCK;
tp->tp_flags |= TPF_LOCKED;
/* got it ! */
break;
}
tp->tp_flags |= TPF_WANT_LOCK;
KPRINTF_DISABLED (("__get_pipe: going to sleep\n"));
if (ix_sleep (&tp->tp_flags, "get_pipe") < 0)
{
KPRINTF_DISABLED (("__get_pipe: interrupted\n"));
Permit ();
setrun (FindTask (0));
goto retry;
}
KPRINTF_DISABLED (("__get_pipe: back from sleep (not interrupted)\n"));
/* have to always recheck whether we really got the lock */
}
Permit ();
}
static inline void
__release_pipe (struct file *f)
{
struct tmp_pipe *tp = f->f_tp;
Forbid ();
if (tp->tp_flags & TPF_WANT_LOCK)
ix_wakeup (&tp->tp_flags);
tp->tp_flags &= ~(TPF_WANT_LOCK|TPF_LOCKED);
Permit ();
}
int
pipe (int pv[2])
{
struct file *f1, *f2;
struct tmp_pipe *tp;
int res, err, omask;
omask = syscall (SYS_sigsetmask, ~0);
res = -1;
err = EMFILE;
if (tp = __pinit ())
{
if (! falloc (&f1, pv))
{
if (! falloc (&f2, pv+1))
{
f1->f_tp = tp;
f1->f_stb.st_mode = 0666 | S_IFCHR;
f1->f_stb.st_size = PIPE_SIZE;
f1->f_stb.st_blksize = 512;
f1->f_flags = FREAD;
f1->f_type = DTYPE_PIPE;
f1->f_read = __pread;
f1->f_write = 0;
f1->f_ioctl = __pioctl;
f1->f_close = __pclose;
f1->f_select = __pselect;
f2->f_tp = tp;
f2->f_stb.st_mode = 0666 | S_IFCHR;
f2->f_stb.st_size = PIPE_SIZE;
f2->f_stb.st_blksize = 512;
f2->f_flags = FWRITE;
f2->f_type = DTYPE_PIPE;
f2->f_read = 0;
f2->f_write = __pwrite;
f2->f_ioctl = __pioctl;
f2->f_close = __pclose;
f2->f_select = __pselect;
res = err =0;
goto ret;
}
f1->f_count = 0;
}
kfree (tp);
}
ret:
syscall (SYS_sigsetmask, omask);
errno = err;
KPRINTF_DISABLED (("&errno = %lx, errno = %ld\n", &errno, errno));
return res;
}
static struct tmp_pipe *
__pinit (void)
{
struct tmp_pipe *tp = (struct tmp_pipe *) kmalloc (sizeof (*tp));
if (tp)
{
tp->tp_flags = 0;
tp->tp_reader = tp->tp_writer = tp->tp_buffer;
}
return tp;
}
static int
__pclose (struct file *f)
{
int omask;
ix_lock_base ();
f->f_count--;
if (f->f_count == 0)
{
if (f->f_read)
f->f_tp->tp_flags |= TPF_NO_READER;
else
f->f_tp->tp_flags |= TPF_NO_WRITER;
if ((f->f_tp->tp_flags & (TPF_NO_READER|TPF_NO_WRITER)) ==
(TPF_NO_READER|TPF_NO_WRITER))
kfree (f->f_tp);
else
ix_wakeup (f->f_tp);
}
ix_unlock_base ();
return 0;
}
static int
__pread (struct file *f, char *buf, int len)
{
int omask = syscall (SYS_sigsetmask, ~0);
int err = errno;
int really_read = 0;
struct tmp_pipe *tp = f->f_tp;
__get_pipe (f);
while (len)
{
if (tp->tp_reader == tp->tp_writer)
{
KPRINTF_DISABLED (("__pread: len == %ld, buffer full\n", len));
if (tp->tp_flags & TPF_NO_WRITER)
{
KPRINTF_DISABLED (("__pread: EOF\n"));
err = 0;
break;
}
if (f->f_flags & FNDELAY)
{
if (! really_read)
{
really_read = -1;
err = EAGAIN;
}
break;
}
else if (really_read)
{
err = 0;
break;
}
else
{
int sleep_rc;
KPRINTF_DISABLED (("__pread: going to sleep.\n"));
/* wait for something to be read or all readers to close */
Forbid ();
/* sigh.. Forbid() is necessary, or the other end may change
the pipe, and in the worst case also settle for sleep(), and
there it is.. deadlock.. */
__release_pipe (f);
/* make write interruptible */
syscall (SYS_sigsetmask, omask);
sleep_rc = ix_sleep (tp, "pwrite");
Permit ();
if (sleep_rc < 0)
setrun (FindTask (0));
omask = syscall (SYS_sigsetmask, ~0);
__get_pipe (f);
continue; /* retry */
}
}
else
{
/* okay, there's something to read from the pipe */
if (tp->tp_reader > tp->tp_writer)
{
/* read till end of buffer and wrap around */
int avail = PIPE_SIZE - (tp->tp_reader - tp->tp_buffer);
int do_read = len < avail ? len : avail;
/* KPRINTF_DISABLED (("__pread-1: reading %ld bytes.\n", do_read)); */
really_read += do_read;
bcopy (tp->tp_reader, buf, do_read);
len -= do_read;
buf += do_read;
tp->tp_reader += do_read;
if (tp->tp_reader - tp->tp_buffer == PIPE_SIZE)
/* wrap around */
tp->tp_reader = tp->tp_buffer;
}
if (len && tp->tp_reader < tp->tp_writer)
{
int avail = tp->tp_writer - tp->tp_reader;
int do_read = len < avail ? len : avail;
/* KPRINTF_DISABLED (("__pread-2: reading %ld bytes.\n", do_read)); */
really_read += do_read;
bcopy (tp->tp_reader, buf, do_read);
tp->tp_reader += do_read;
len -= do_read;
buf += do_read;
}
}
ix_wakeup (tp);
}
__release_pipe (f);
syscall (SYS_sigsetmask, omask);
errno = err;
KPRINTF_DISABLED (("&errno = %lx, errno = %ld\n", &errno, errno));
return really_read;
}
static int
__pwrite (struct file *f, char *buf, int len)
{
int omask = syscall (SYS_sigsetmask, ~0);
int err = errno;
int really_written = 0;
struct tmp_pipe *tp = f->f_tp;
__get_pipe (f);
while (len)
{
if (tp->tp_flags & TPF_NO_READER)
{
KPRINTF_DISABLED (("__pwrite: SIGPIPE\n"));
really_written = -1;
err = EPIPE;
/* this is something no `real' Amiga pipe handler will do ;-)) */
_psignal (FindTask (0), SIGPIPE);
break;
}
/* buffer full ?? */
if (tp->tp_reader == tp->tp_writer + 1
|| (tp->tp_reader == tp->tp_buffer
&& tp->tp_writer == tp->tp_buffer + PIPE_SIZE - 1))
{
KPRINTF_DISABLED (("__pwrite: buffer full, len == %ld\n", len));
if (f->f_flags & FNDELAY)
{
if (! really_written)
{
really_written = -1;
err = EAGAIN;
}
break;
}
else
{
int sleep_rc;
KPRINTF_DISABLED (("__pwrite: going to sleep\n"));
/* wait for something to be read or all readers to close */
Forbid ();
/* sigh.. Forbid() is necessary, or the other end may change
the pipe, and in the worst case also settle for sleep(), and
there it is.. deadlock.. */
__release_pipe (f);
/* make write interruptible */
syscall (SYS_sigsetmask, omask);
sleep_rc = ix_sleep (tp, "pwrite");
Permit ();
if (sleep_rc < 0)
setrun (FindTask (0));
omask = syscall (SYS_sigsetmask, ~0);
__get_pipe (f);
continue; /* retry */
}
}
else
{
/* okay, there's some space left to write to the pipe */
if (tp->tp_writer >= tp->tp_reader)
{
/* write till end of buffer */
int avail = PIPE_SIZE - 1 - (tp->tp_writer - tp->tp_buffer);
int do_write;
if (tp->tp_reader > tp->tp_buffer)
avail++;
do_write = len < avail ? len : avail;
/* KPRINTF_DISABLED (("__pwrite-1: writing %ld bytes.\n", do_write)); */
really_written += do_write;
bcopy (buf, tp->tp_writer, do_write);
len -= do_write;
buf += do_write;
tp->tp_writer += do_write;
if (tp->tp_writer - tp->tp_buffer == PIPE_SIZE)
tp->tp_writer = tp->tp_buffer;
}
if (tp->tp_writer < tp->tp_reader - 1)
{
int avail = tp->tp_reader - tp->tp_writer - 1;
int do_write = len < avail ? len : avail;
/* KPRINTF_DISABLED (("__pwrite-2: writing %ld bytes.\n", do_write)); */
really_written += do_write;
bcopy (buf, tp->tp_writer, do_write);
tp->tp_writer += do_write;
len -= do_write;
buf += do_write;
}
}
ix_wakeup (tp);
}
__release_pipe (f);
syscall (SYS_sigsetmask, omask);
errno = err;
KPRINTF_DISABLED (("&errno = %lx, errno = %ld\n", &errno, errno));
return really_written;
}
static int
__pselect (struct file *f, int select_cmd, int io_mode)
{
struct tmp_pipe *tp = f->f_tp;
/* I currently only check whether io is possible, no setup needed.
This would be quite different if select() waited the given timeout,
and wouldn't split the timeout into smaller slices */
if (select_cmd == SELCMD_CHECK || select_cmd == SELCMD_POLL)
{
/* we support both, read and write checks (hey, something new ;-)) */
if (io_mode == SELMODE_IN)
return tp->tp_reader != tp->tp_writer;
else if (io_mode == SELMODE_OUT)
return !(tp->tp_reader == tp->tp_writer + 1
|| (tp->tp_reader == tp->tp_buffer
&& tp->tp_writer == tp->tp_buffer + PIPE_SIZE - 1));
}
return 0;
}
static int
__pioctl (struct file *f, unsigned int cmd, unsigned int inout,
unsigned int arglen, unsigned int arg)
{
int omask;
int result;
struct tmp_pipe *tp = f->f_tp;
omask = syscall (SYS_sigsetmask, ~0);
__get_pipe (f);
switch (cmd)
{
case FIONREAD:
{
unsigned int *pt = (unsigned int *)arg;
if (tp->tp_reader < tp->tp_writer)
*pt = tp->tp_writer - tp->tp_reader;
else if (tp->tp_reader > tp->tp_writer)
*pt = PIPE_SIZE - (tp->tp_reader - tp->tp_writer);
else
*pt = 0;
result = 0;
break;
}
case FIONBIO:
{
result = f->f_flags & FNDELAY ? 1 : 0;
if (*(unsigned int *)arg)
f->f_flags |= FNDELAY;
else
f->f_flags &= ~FNDELAY;
/* I didn't find it documented in a manpage, but I assume, we
* should return the former state, not just zero.. */
break;
}
case FIOASYNC:
{
/* DOESN'T WORK YET */
int flags = *(unsigned long*)arg;
result = f->f_flags & FASYNC ? 1 : 0;
if (flags)
f->f_flags |= FASYNC;
else
f->f_flags &= ~FASYNC;
/* ATTENTION: have to call some function here in the future !!! */
/* I didn't find it documented in a manpage, but I assume, we
* should return the former state, not just zero.. */
break;
}
case FIOCLEX:
case FIONCLEX:
case FIOSETOWN:
case FIOGETOWN:
/* this is no error, but nevertheless we don't take any actions.. */
result = 0;
break;
}
__release_pipe (f);
syscall (SYS_sigsetmask, omask);
return result;
}